<!--
 * @Author: rxn 1003312535@qq.com
 * @Date: 2024-07-31 11:03:30
 * @LastEditors: rxn 1003312535@qq.com
 * @LastEditTime: 2024-08-05 14:49:56
 * @FilePath: \vue3-progect\src\views\example\three\case\flyLine\flyLine01.vue
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<!-- three.js 飞线功能开发 -->
<template>
	<div class="container" id="container"></div>
</template>
<script setup>
import { onMounted } from "vue";
//-
import * as THREE from "three";
import TWEEN from "@tweenjs/tween.js"; //1. tweenjs创建threejs动画

import { OrbitControls } from "three/addons/controls/OrbitControls.js";
//-
onMounted(() => {
	//-
	const container = document.getElementById("container");
	const instance = new BaseInfo(container);
});

class BaseInfo {
	container = null;
	scene = null;
	camera = null;
	renderer = null;

	constructor(container) {
		this.container = container;
		this.scene = new THREE.Scene();
		this.camera = this.createCamera();
		this.renderer = this.createRenderer();
		this.animate();
		//--
		const ambient = new THREE.AmbientLight("#ffffff");
		this.scene.add(ambient);
		this.create();
		this.initHelper();

		const path = new flyLine();
		path.createCatmullRomCurve3([-20, 0, 0], [20, 0, 0], { y: 20 });
		//-
		const line = path.createLine();
		line.position.z = -10;
		this.scene.add(line);
		//-
		this.flyLine = path.createFlyLine();
		this.flyLine.position.z = -5;
		this.scene.add(this.flyLine);
		//-
		const textureLine = path.createTextureLine();
		this.textureLine = textureLine.clone();
		this.textureLine.geometry = textureLine.geometry;
		this.scene.add(this.textureLine);
		//-
		const ShaderLine = path.createShaderLine();
		ShaderLine.position.z = 5;
		this.scene.add(ShaderLine);
	}
	//-创建相机
	createCamera() {
		const camera = new THREE.PerspectiveCamera(
			90,
			this.container.offsetWidth / this.container.offsetHeight,
			0.1,
			1000
		);
		camera.position.set(30, 30, 30);
		return camera;
	}
	//-创建渲染实例
	createRenderer() {
		const renderer = new THREE.WebGLRenderer({ antialias: true });
		renderer.setSize(this.container.offsetWidth, this.container.offsetHeight);
		this.container.appendChild(renderer.domElement);
		return renderer;
	}
	//-执行渲染
	render() {
		this.renderer.render(this.scene, this.camera);
	}
	//-循环渲染
	animate() {
		TWEEN.update();
		requestAnimationFrame(this.animate.bind(this));
		this.render();
	}
	/**
	 * -------------------------------------------------
	 * 功能
	 * -------------------------------------------------
	 */
	create() {
		const geometry = new THREE.PlaneGeometry(100, 100);
		const material = new THREE.MeshBasicMaterial({ color: 0xaaaaaa });
		const mesh = new THREE.Mesh(geometry, material);
		mesh.rotateX(-Math.PI / 2);
		this.scene.add(mesh);
	}

	/**
	 * -------------------------------------------------
	 * 辅助功能
	 * -------------------------------------------------
	 */
	/**
	 * -------------------------------------------------
	 * 辅助功能
	 * -------------------------------------------------
	 */
	initHelper() {
		this.createAxesHelper();
		this.OrbitControlsHelper();
	}
	//-辅助-axesHelper
	createAxesHelper() {
		const axesHelper = new THREE.AxesHelper(200);
		this.scene.add(axesHelper);
	}
	//-辅助-相机轨道运动
	OrbitControlsHelper() {
		const controls = new OrbitControls(this.camera, this.renderer.domElement);
		controls.update();
	}
}
class flyLine {
	curve = null;
	constructor(container) {}
	/**
	 * 方法一：创建曲线
	 * start array 开始坐标
	 * end array 结束坐标
	 * depth object 弧线高度 {方向：高度}， {x:20},{y:20},{z:200}
	 */
	createCatmullRomCurve3(start, end, depth) {
		// p1、p3轨迹线起始点坐标
		const p1 = new THREE.Vector3(...start);
		const p3 = new THREE.Vector3(...end);
		// 计算p1和p3的中点坐标
		const x2 = (p1.x + p3.x) / 2;
		const y2 = (p1.y + p3.y) / 2;
		const z2 = (p1.z + p3.z) / 2;
		function depthHandler(value) {
			if (value !== "undefined" || value !== "null") {
				return value;
			} else {
				return false;
			}
		}
		const p2 = new THREE.Vector3(
			depthHandler(depth.x) || x2,
			depthHandler(depth.y) || y2,
			depthHandler(depth.z) || z2
		);
		const arr = [p1, p2, p3];
		// 三维样条曲线
		this.curve = new THREE.CatmullRomCurve3(arr);
	}
	// /**
	//  * 方法二：创建曲线-创建地图飞线用到的，默认飞线高度为 Z轴
	//  * data = [{center: [x,y],depth: 10}, {center: [x,y], depth: 5 }]
	//  */
	// createCatmullRomCurve3(data) {
	// 	const [startCenterX, startCenterY] = data[0].center;
	// 	const startDepth = data[0].depth;
	// 	const [endCenterX, endCenterY] = data[1].center;
	// 	const endDepth = data[1].depth;
	// 	// p1、p2轨迹线起始点坐标
	// 	const p1 = new THREE.Vector3(startCenterX, -startCenterY, startDepth);
	// 	const p2 = new THREE.Vector3(endCenterX, -endCenterY, endDepth);
	// 	const x = (p2.x + p1.x) / 2;
	// 	const y = (p2.y + p1.y) / 2;
	// 	const z = (endDepth + startDepth) / 2 + 1;
	// 	const p = new THREE.Vector3(x, y, z);

	// 	const arr = [p1, p, p2];
	// 	// 三维样条曲线
	// 	this.curve = new THREE.CatmullRomCurve3(arr);
	// }
	//-创建连线
	createLine(segment = 50) {
		const points = this.curve.getPoints(segment);
		const geometry = new THREE.BufferGeometry();
		geometry.setFromPoints(points);
		const material = new THREE.LineBasicMaterial({ color: 0x999999 });
		const lineMesh = new THREE.Line(geometry, material);
		return lineMesh;
	}
	//-创建飞线
	/**
	 * length num 多少个等间距
	 */
	createFlyLine(length = 5, segment = 200) {
		const points = this.curve.getSpacedPoints(segment);
		const slicePoints = points.slice(0, length);

		const geometry = new THREE.BufferGeometry();
		geometry.setFromPoints(slicePoints);
		// console.log(slicePoints, "slicePoints");
		// return;
		const colorArr = [];
		for (let i = 0; i < slicePoints.length; i++) {
			const color1 = new THREE.Color("#00FFFF"); // 线颜色
			const color2 = new THREE.Color("#FFFF00"); // 飞痕颜色
			// 飞痕渐变色
			let color = color1.lerp(color2, i / 5);
			colorArr.push(color.r, color.g, color.b);
		}
		const colorAttribute = new THREE.BufferAttribute(
			new Float32Array(colorArr),
			3
		);
		geometry.setAttribute("color", colorAttribute);
		geometry.attributes.position.needsUpdate = true;
		const material = new THREE.LineBasicMaterial({
			vertexColors: true
		});
		this.createFlyLineAnimate(geometry, points);
		const lineMesh = new THREE.Line(geometry, material);
		return lineMesh;
	}
	//-创建飞线动画
	createFlyLineAnimate(geometry, points) {
		// console.log(points.length, "points--动画");
		// 创建动画
		let tween = new TWEEN.Tween({ index: 0 })
			.to({ index: points.length }, this.cycle || 1000)
			.onUpdate(function (t) {
				let movedLineGeom = geometry;
				let id = Math.ceil(t.index);
				let pointsList = points.slice(id, id + 10); //从曲线上获取一段
				movedLineGeom && movedLineGeom.setFromPoints(pointsList);
				movedLineGeom.attributes.position.needsUpdate = true;
			})
			.repeat(Infinity);
		tween.start();
	}
	//-创建贴图功能
	createTextureLine() {
		const url = new URL("@/assets/image/flyLine/jiantou.png", import.meta.url);
		const textLoader = new THREE.TextureLoader();
		const texture = textLoader.load(url);
		texture.repeat.set(10, 2);
		texture.needsUpdate = true;
		// 设置.wrapS也就是U方向，纹理映射模式(包裹模式)
		texture.wrapS = THREE.RepeatWrapping; //对应offste.x偏移
		// 设置.wrapT也就是V方向，纹理映射模式
		texture.wrapT = THREE.RepeatWrapping; //对应offste.y偏移
		const geometry = new THREE.TubeGeometry(this.curve, 40, 1, 25);
		const material = new THREE.MeshLambertMaterial({
			side: true,
			color: 0xffffff,
			map: texture
		});
		this.createTextureLineAnimate(texture);
		const mesh = new THREE.Mesh(geometry, material);
		return mesh;
	}
	//-创建贴图动画
	createTextureLineAnimate(texture) {
		let tween = new TWEEN.Tween({ x: 0 })
			.to({ x: 100 }, 1000)
			.onUpdate(function (t) {
				texture.offset.x -= 0.01;
			})
			.repeat(Infinity);
		tween.start();
	}
	//-创建着色器
	createShaderLine(segment = 200) {
		const points = this.curve.getSpacedPoints(segment);
		let indexList = [];
		points.forEach((item, index) => {
			indexList.push(index);
		});
		const geometry = new THREE.BufferGeometry();
		geometry.setFromPoints(points);
		geometry.setAttribute(
			"aIndex",
			new THREE.Float32BufferAttribute(indexList, 1)
		);

		const vertexShader = `
    attribute float aIndex;
    uniform float uTime;
    uniform vec3 vColor;
    varying float vSize;

    void main() {
      gl_Position = projectionMatrix*modelViewMatrix* vec4(position,1.0);
      if(aIndex < uTime + 10.0 && aIndex > uTime - 10.0) {
        vSize = (aIndex + 5.0 - uTime) ;
      }else {
      }
      gl_PointSize = vSize;
    }
    `;
		const fragmentShader = `
        varying float vSize;
        uniform vec3 vColor;
        void main(){
            if(vSize<=0.0){
                gl_FragColor = vec4(1.0,0.0,0.0,0.0);
            }else{
                gl_FragColor = vec4(vColor,1.0);
            }

        }
      `;

		const material = new THREE.ShaderMaterial({
			vertexShader,
			fragmentShader,
			transparent: true, //允许透明
			side: true,
			vertexColors: true, //允许设置使用顶点颜色渲染
			uniforms: {
				uTime: { value: 0 },
				vColor: { value: new THREE.Color(0x00ffff) }
			}
		});
		this.createShaderLineAnimate(material);
		const mesh = new THREE.Points(geometry, material);
		return mesh;
	}
	//-创建着色器动画
	createShaderLineAnimate(material) {
		let tween = new TWEEN.Tween({ index: 0 })
			.to({ index: 200 }, 1000)
			.onUpdate(function (t) {
				// console.log(t, "t");
				let id = Math.ceil(t.index);
				// console.log(id, "id");
				material.uniforms.uTime.value = id;
			})
			.repeat(Infinity);
		tween.start();
	}
}
</script>
<style lang="less" scoped>
//-
.container {
	width: 100%;
	height: 100%;
}
</style>
